/*!
 * \file input_impl.cpp - see common.hpp for copyright info.
 */

#include "common.hpp"

#define ANALOGUE_NUB_MAX    128
/******************************************************************************/
/*!
 * \brief Wrapper around Allegro's poll_joystick() to make the API 'feel`
 * more consistent...
 */
void INPUT::poll()
{
    poll_joystick();
    poll_keyboard();
}

/******************************************************************************/
/*!
 * \brief Create a new INPUT object and bind it to the requested joypad.
 *
 * \note No error is generated if an INPUT is bound to a non-existant joystick;
 * there just won't be any way for the user to communicate with it.
 *
 * \note If 0 is passed as an argument, that is, this is the first INPUT,
 * key controls will also be listened for.
 *
 * \bug The case of Player 1 on the keyboard and Player 2 on a joypad isn't
 * supported (yet).
 */
INPUT::INPUT(int which)
{
    this->which = which;

    fire_ok     = BTN_IDLE;
    thrust      = BTN_IDLE;
    roll_cancel = BTN_IDLE;
    up          = BTN_IDLE;
    down        = BTN_IDLE;
}

/******************************************************************************/
/*!
 * \brief Gets the current state of the accept/okay/fire button (button 1 on
 * a joypad, 'Z' on a keyboard).
 */
INPUT::BUTTON_STATE INPUT::get_fire_ok_state()
{
    bool is_button_down_now;

    is_button_down_now = joy[which].button[0].b;

    if (which == 0)
        is_button_down_now |= key[KEY_Z];

    // was the button being pressed in the previous update?
    if (is_button_down_now)
    {
        if (fire_ok != BTN_IDLE)
            fire_ok = BTN_HELD;
        else
            fire_ok = BTN_PRESSED;
    }
    else
        fire_ok = BTN_IDLE;

    return fire_ok;
}

/******************************************************************************/
/*!
 * \brief Gets the current state of the thruster button (button 2 on
 * a joypad, 'X' on a keyboard).
 */
INPUT::BUTTON_STATE INPUT::get_thrust_state()
{
    bool is_button_down_now;

    is_button_down_now = joy[which].button[1].b;

    if (which == 0)
        is_button_down_now |= key[KEY_X];

    // was the button being pressed in the previous update?
    if (is_button_down_now)
    {
        if (thrust != BTN_IDLE)
            thrust = BTN_HELD;
        else
            thrust = BTN_PRESSED;
    }
    else
        thrust = BTN_IDLE;

    return thrust;
}

/******************************************************************************/
/*!
 * \brief Gets the current state of the special-move/escape/cancel button
 * (button 3 on a joypad, 'C' on a keyboard).
 */
INPUT::BUTTON_STATE INPUT::get_roll_cancel_state()
{
    bool is_button_down_now;

    is_button_down_now = joy[which].button[2].b;

    if (which == 0)
        is_button_down_now |= key[KEY_C];

    // was the button being pressed in the previous update?
    if (is_button_down_now)
    {
        if (roll_cancel != BTN_IDLE)
            roll_cancel = BTN_HELD;
        else
            roll_cancel = BTN_PRESSED;
    }
    else
        roll_cancel = BTN_IDLE;

    return roll_cancel;
}

/******************************************************************************/
/*!
 * \brief Gets the current state of the up button (analogue-up on a joypad,
 * up-arrow on a keyboard).
 */
INPUT::BUTTON_STATE INPUT::get_up_state()
{
    bool is_button_down_now;

    is_button_down_now = (joy[which].stick[0].axis[1].pos < (-INPUT::DEAD_ZONE));

    if (which == 0)
        is_button_down_now |= key[KEY_UP];

    // was the button being pressed in the previous update?
    if (is_button_down_now)
    {
        if (up != BTN_IDLE)
            up = BTN_HELD;
        else
            up = BTN_PRESSED;
    }
    else
        up = BTN_IDLE;

    return up;
}

/******************************************************************************/
/*!
 * \brief Gets the current state of the down button (analogue-down on a joypad,
 * down-arrow on a keyboard).
 */
INPUT::BUTTON_STATE INPUT::get_down_state()
{
    bool is_button_down_now;

    is_button_down_now = (joy[which].stick[0].axis[1].pos > INPUT::DEAD_ZONE);

    if (which == 0)
        is_button_down_now |= key[KEY_DOWN];

    // was the button being pressed in the previous update?
    if (is_button_down_now)
    {
        if (down != BTN_IDLE)
            down = BTN_HELD;
        else
            down = BTN_PRESSED;
    }
    else
        down = BTN_IDLE;

    return down;
}

/******************************************************************************/
/*!
 * \brief Returns the distance the analogue nub is from the centre as a
 * fraction between zero and one along the x axis.
 */
float INPUT::get_analogue_x()
{
    return (joy[which].stick[0].axis[0].pos / (float)ANALOGUE_NUB_MAX);
}

/******************************************************************************/
/*!
 * \brief Returns the distance the analogue nub is from the centre as a
 * fraction between zero and one along the y axis.
 */
float INPUT::get_analogue_y()
{
    return (joy[which].stick[0].axis[1].pos / (float)ANALOGUE_NUB_MAX);
}

/******************************************************************************/
/*!
 * \brief Returns the angle the nub is currently being pressed in (in radians).
 * \note This value is in <u>radians</u> and from the standard position
 * (that is,if the stick were pressed all the way to the right with no up or
 * down component, that would be an angle of 0 radians.
 */
float INPUT::get_analogue_angle()
{
    // this looks wrong but it isn't - don't change it unless you know what you're doing
    return atan2f(-joy[which].stick[0].axis[1].pos, -joy[which].stick[0].axis[0].pos) - M_PI_2;
}

/******************************************************************************/
/*!
 * \brief Returns the distance the nub is currently being pressed away from
 * the centre
 */
float INPUT::get_analogue_distance()
{
    return hypotf(joy[which].stick[0].axis[0].pos, joy[which].stick[0].axis[1].pos);
}
